const vec2 = glMatrix.vec2;
const vec3 = glMatrix.vec3;
const mat4 = glMatrix.mat4;

let vs = `
        attribute vec3 a_position;
uniform mat4 projMatrix;
uniform mat4 mvMatrix;

void main() {
    gl_Position = projMatrix * mvMatrix * vec4(a_position,1.0);
}`;

let fs = `
        precision mediump float;
uniform vec4 u_color;

void main() {
    gl_FragColor = u_color;
}
`;


let canvas, gl;
let width, height;
let vao,vbo;
let shaderInfo;
let projMatrix = mat4.create();
let viewMatrix = mat4.create();
let worldMatrix = mat4.create();
let mvMatrix = mat4.create();
const fovy = 60;
const near = 0.1;
const far = 1000;

window.onload = function () {
    console.log("----- window.onload");

    canvas = document.getElementById("game");
    width = canvas.width = canvas.clientWidth;
    height = canvas.height = canvas.clientHeight;

    gl = canvas.getContext('webgl');

    // 使用vao扩展
    useExt("OES_vertex_array_object");

    shaderInfo = buildShader(vs, fs);
    createUniforms(shaderInfo, ['projMatrix',
        'mvMatrix', 'u_color']);
    createAttribs(shaderInfo,['a_position']);

    // init matrixs
    mat4.perspective(projMatrix,fovy * Math.PI/180,width/height,near,far);
    mat4.lookAt(viewMatrix,vec3.fromValues(0,2,5),vec3.create(),vec3.fromValues(0,1,0));

    let vertices = [
        -1, 0, 0,
        1, 0, 0,
        0, 1, 0
    ];

    vao = gl.createVertexArrayOES();
    gl.bindVertexArrayOES(vao);
    vbo = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER,vbo);
    gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(vertices),gl.STATIC_DRAW);
    gl.enableVertexAttribArray(shaderInfo.attribs["a_position"]);
    gl.vertexAttribPointer(shaderInfo.attribs["a_position"],3,gl.FLOAT,false,0,0);
    gl.bindVertexArrayOES(null);
    loop();
};
function loop() {
    requestAnimationFrame(loop);
    frame();
}
function frame() {
    gl.clear(gl.COLOR_BUFFER_BIT);

    gl.useProgram(shaderInfo.program);

    mat4.mul(mvMatrix,viewMatrix,worldMatrix);
    gl.uniformMatrix4fv(shaderInfo.uniforms["projMatrix"],false,projMatrix);
    gl.uniformMatrix4fv(shaderInfo.uniforms["mvMatrix"],false,mvMatrix);
    gl.uniform4f(shaderInfo.uniforms["u_color"],1,0,0,1);
    gl.bindVertexArrayOES(vao);
    gl.drawArrays(gl.TRIANGLES,0,3);

    gl.useProgram(null);
}

function isFunction(obj) {
    return Object.prototype.toString.call(obj) === '[object Function]';
}

function useExt(name) {
    let ext = gl.getExtension(name);
    if (!ext) {
        throw `load ext:${name} failed!`
    }

    for (let k in ext) {
        if (isFunction(ext[k]) && !gl.hasOwnProperty(k)) {
            gl[k] = ext[k].bind(ext);
            console.log("===== 添加扩展方法:", k);
        }
    }
}

function buildShader(vs, fs) {
    let vsShader = createShader(gl.VERTEX_SHADER, vs);
    let fsShader = createShader(gl.FRAGMENT_SHADER, fs);
    let program = gl.createProgram();
    gl.attachShader(program, vsShader);
    gl.attachShader(program, fsShader);
    gl.linkProgram(program);

    let msg = gl.getProgramInfoLog(program);
    if (msg.length > 0) {
        throw msg
    }

    gl.detachShader(program, vsShader);
    gl.detachShader(program, fsShader);

    return {
        vs: vsShader,
        fs: fsShader,
        program: program,
        uniforms: {},
        attribs: {},
    }
}

function createUniforms(shaderInfo, uniformNames) {
    uniformNames.forEach(name => {
        let location = gl.getUniformLocation(shaderInfo.program, name);
        if (!location) {
            throw `getUniformLocation for ${name} failed!`
        }
        shaderInfo.uniforms[name] = location;
    });
}

function createAttribs(shaderInfo, attribs) {
    attribs.forEach(name => {
        let location = gl.getAttribLocation(shaderInfo.program, name);
        if (location < 0) {
            throw `getAttribLocation for ${name} failed!`
        }
        shaderInfo.attribs[name] = location;
    })
}

function createShader(shaderType, source) {
    let shader = gl.createShader(shaderType);
    if (shader <= 0) {
        throw `create shaderType:${shaderType} error!`
    }
    gl.shaderSource(shader, source);
    gl.compileShader(shader);

    let msg = gl.getShaderInfoLog(shader);
    if (msg.length > 0) {
        throw msg
    }
    return shader;
}
